#include <stdio.h>
#include <stdlib.h>
#include <cctype>
#include <string>
#include <iostream>
#include <vector>

// Type Declarations

enum PrintBehavior {
    CloseAtSameLine,
    CloseAtOtherLine
};

enum SymType {
    IfSym,
    ElseSym,
    EndifSym,
    EndSym,
    Ident,
    Number,
    Operator,
};

typedef std::string  Symbol;
typedef std::vector<Symbol>  SymTab;
typedef SymTab* TabPtr;

/* Constant Declarations */
char Look;                    // Lookahead Character
const int buf_len = 64;
int DLevel = 0; // Debug log level

SymType Token; // Current Token
Symbol Value;  // String Token of Look

// Definition of Keywords and Token Types
SymTab KWlist{
    "IF", "ELSE", "ENDIF", "END"};

// Table Lookup

// If the input string matches a table entry, return the entry index.
// If not, return a -1.

int Lookup(const TabPtr T, const Symbol& s) {
    for (int i = 0; i < T->size(); ++i) {
        if (s == (*T)[i]) {
            return i;
        }
    }
    return -1;
}

/* Print Token. Usage: PrintEnter(Token, CloseAtOtherLine/CloseAtSameLine).
   If CloseAtOtherLine, when going out of the Token name scope, you must call PrintLeave().
 */
void PrintEnter(const char* s, PrintBehavior behavior = CloseAtOtherLine) {
    for (int i = 0; i < DLevel; ++i) {
        printf("\t");
    }
    printf("<%s", s);
    if (behavior == CloseAtSameLine) {
        printf(">\n");
    }
    else {
        printf("\n");
        ++DLevel;
    }
}

void PrintLeave() {
    for (int i = 1; i < DLevel; ++i) {
        printf("\t", DLevel);
    }
    printf(">\n");
    --DLevel;
}

/* Read New Character From Input Stream */
void GetChar() {
  Look = getchar();
}

/* Report an Error */
template<typename T>
void WriteLn(const T& t) {
    std::cout << t << std::endl;
}

void Error(const std::string& s) {
  std::cout << std::endl;
  std::cout << "Error: " << s << "." << std::endl;
}

/* Report Error and Halt */
void Abort(const std::string& s) {
    Error(s);
    exit(0);
}

/* Report What Was Expected */
void Expected(const std::string& s) {
    Abort(s + " Expected");
}

/* Recognize White Space */
int IsWhite(char c) {
    if (c==' ' ||
        c=='\t') {
        return 1;
    }
    else {
        return 0;
    }
    // return isspace(c);
}

/* Skip Over Leading White Space */
void SkipWhite() {
  while (IsWhite(Look)) {
	GetChar();
  }
}

// Skip Over a Comma
void SkipComma() {
    SkipWhite();
    if (Look == ',') {
        GetChar();
        SkipWhite();
    }
}

// Recognize Any Operator
int IsOp(char c) {
    switch (c) {
    case '+':
    case '-':
    case '*':
    case '/':
    case '<':
    case '>':
    case ':':
    case '=':
        return 1;
    default:
        return 0;
    }
}

// Skip a CRLF
void Fin() {
    while ((Look == '\n') || (Look == '\r')) {
        GetChar();
    }
    SkipWhite();
}

/* Match a Specific Input Character */
void Match(char x) {
  if (Look == x) {
	GetChar();
  }
  else {
	char buf[buf_len] = {0};
	sprintf(buf, "'''' %c ''''", x);
	Expected(buf);
  }
}

/* Recognize an Alpha Character  */
int IsAlpha(char c) {
  return isalpha(c);
}

/* Recognize a Decimal Digit */
int IsDigit(char c) {
  return isdigit(c);
}

/* Recognize an Alphanumeric Character */
int IsAlNum(char c) {
    return (IsAlpha(c) || IsDigit(c));
}

/* Recognize an Addop */
int IsAddop(char c) {
  if ((c == '+') ||
	  (c == '-')) {
	return 1;
  }
  else {
	return 0;
  }
}

/* Get an Identifier */
void GetName() {
  if (!IsAlpha(Look)) {
	Expected("Name");
  }

  Value.clear();
  while (IsAlNum(Look)) {
      Value += std::toupper(Look);
      GetChar();
  }

  int k = Lookup(&KWlist, Value);
  if (k == -1) {
      Token = SymType::Ident;
  }
  else {
      Token = SymType(k);
  }

  SkipWhite();
}

/* Get a Number */
void GetNum() {
  if (!IsDigit(Look)) {
	Expected("Integer");
  }

  Value.clear();
  while (IsDigit(Look)) {
      Value += (Look);
      GetChar();
  }

  Token = SymType::Number;
  
  SkipWhite();
}

// Get a Operator
void GetOp() {
    if (!IsOp(Look)) {
        Expected("Operator");
    }

    Value.clear();
    while (IsOp(Look)) {
        Value += (Look);
        GetChar();
    }

    Token = SymType::Operator;
    
    SkipWhite();
}

// Lexical Scanner
void Scan() {
    Fin();
    
    if (IsAlpha(Look)) {
        GetName();
    }
    else if (IsDigit(Look)) {
        GetNum();
    }
    else if (IsOp(Look)) {
        GetOp();
    }
    else {
        Value = Look;
        Token = SymType::Operator;
        GetChar();
    }

    SkipComma();
}

/* Output a String with Tab */
void EmitLn(const std::string& s) {
  std::cout << "\t " << s << std::endl;
}

/* Initialize */
void Init() {
  GetChar();
}

/* Main Program */
int main(int argc, char** argv) {
  Init();

  do {
      Scan();
      switch (Token) {
      case SymType::Ident: {
          std::cout << "Ident ";
          break;
      }
      case SymType::Number: {
          std::cout << "Number ";
          break;
      }
      case SymType::Operator: {
          std::cout << "Operator ";
          break;
      }
      default:
          std::cout << "Keyword ";
          break;
      }
      WriteLn(Value);
  } while (Token != SymType::EndSym);
  
  return 0;
}
